﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using Core.Base;
using Core.Interfaces;
using Global;
using Global.Mgr;
using UnityEngine;

namespace Core.Mgrs
{
    /// <summary>
    /// 计时器管理器（基于协程）
    /// </summary>
    public class TimerMgr : BaseMgr
    {
        private int timerId = 0;
        private Dictionary<int, Coroutine> timerCoroutines = new Dictionary<int, Coroutine>();
        private List<TimerWrap> timerWraps = new List<TimerWrap>();

        /// <summary>
        /// 启动计时器
        /// </summary>
        /// <param name="cancellationToken">用于自动回收</param>
        /// <param name="callBack">计时器事件</param>
        /// <param name="delay">调用延迟</param>
        /// <param name="doTimes">调用次数，-1表示无限循环</param>
        /// <param name="repeatInterval">调用间隔</param>
        /// <returns>Timer Id</returns>
        public int StartTimer(Action callBack, CancellationToken cancellationToken, float delay = 0f, int doTimes = 1,
            float repeatInterval = 1f)
        {
            timerId++;
            var coroutine =
                KKGL.MainBehaviour.StartCoroutine(TimeCoroutine(timerId, callBack, delay, doTimes,
                    repeatInterval));
            timerCoroutines.Add(timerId, coroutine);
            timerWraps.Add(new TimerWrap(timerId, cancellationToken));
            return timerId;
        }

        /// <summary>
        /// 手动结束计时器
        /// </summary>
        public void EndTimer(int timerId)
        {
            if (timerCoroutines.TryGetValue(timerId, out var coroutine))
            {
                KKGL.MainBehaviour.StopCoroutine(coroutine);
                timerCoroutines.Remove(timerId);
            }
        }

        private IEnumerator TimeCoroutine(int timerId, Action callBack, float delay = 0f, int doTimes = 1,
            float repeatInterval = 1f)
        {
            float timeTick = repeatInterval;
            int finishCount = 0;
            bool isLoop = doTimes == -1;
            yield return new WaitForSeconds(delay);

            if (isLoop)
            {
                while (true)
                {
                    if (timeTick >= repeatInterval)
                    {
                        callBack?.Invoke();
                        timeTick = 0f;
                    }

                    timeTick += Time.deltaTime;
                    yield return null;
                }
            }
            else
            {
                while (finishCount < doTimes)
                {
                    if (timeTick >= repeatInterval)
                    {
                        callBack?.Invoke();
                        timeTick = 0f;
                        finishCount++;
                    }

                    timeTick += Time.deltaTime;
                    yield return null;
                }
            }

            EndTimer(timerId);
        }

        public void OnCancelToken()
        {
            for (int i = 0; i < timerWraps.Count; i++)
            {
                var timerWrap = timerWraps[i];
                if (timerWrap.CancellationToken.IsCancellationRequested)
                {
                    EndTimer(timerWrap.TimerId);
                    timerWraps.Remove(timerWrap);
                }
            }
        }

        private sealed class TimerWrap
        {
            public int TimerId { get; private set; }
            public CancellationToken CancellationToken { get; private set; }

            public TimerWrap(int timerId, CancellationToken cancellationToken)
            {
                TimerId = timerId;
                CancellationToken = cancellationToken;
            }
        }
    }
}